package algorithm;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

public class Mask {
	private BufferedImage mask;
	private Point origin;
	private Point originInf;
	private Hashtable<Integer,List<Integer>> area;
	
	
	
	
	public Mask(BufferedImage mask) {
		super();
		this.mask = mask;
		
		parseMask();
	}

	private void parseMask() {
		
		this.area = getMaskArea(mask);
		this.origin = getOrigin(area);
		this.originInf = getOriginInf(area);
		
	}

	/**
	 * Este metodo retorna el punto origen inferior del area de la mascara
	 * @param area
	 * @return Point
	 */
	private Point getOriginInf(Hashtable<Integer,List<Integer>> area) {
		Point p = new Point();
		int maxW=-1;
		int maxH=-1;
		int parcialW=0;
		int parcialH=0;
		
		Enumeration<Integer> e = area.keys();
		
		while (e.hasMoreElements())
		{
			Integer x = e.nextElement();
			parcialW=x.intValue();
			if (parcialW > maxW)
				maxW=parcialW;
			for (int i = 0; i < area.get(x).size(); i++) {
				parcialH= area.get(x).get(i).intValue();
				if (parcialH > maxH)
					maxH=parcialH;
			}
		}

		p.setLocation(maxW, maxH);
		return p;
		
	}

	/**
	 * Este metodo retorna el punto origen del area de la mascara
	 * @param area
	 * @return Point
	 */
	private Point getOrigin(Hashtable<Integer,List<Integer>> area) {
		Point p = new Point();
		int maxW=32768;
		int maxH=32768;
		int parcialW=0;
		int parcialH=0;
		
		Enumeration<Integer> e = area.keys();
		
		while (e.hasMoreElements())
		{
			Integer x = e.nextElement();
			parcialW=x.intValue();
			if (parcialW < maxW)
				maxW=parcialW;
			for (int i = 0; i < area.get(x).size(); i++) {
				parcialH= area.get(x).get(i).intValue();
				if (parcialH < maxH)
					maxH=parcialH;
			}
		}

		p.setLocation(maxW, maxH);
		return p;
		
	}
	
	/**
	 * Este metodo retorna true si las coordenadas del pixel 
	 * que es pasado por parametro estan dentro del area de la mascara.
	 * De lo contrario retorna false.
	 * @param j
	 * @param i
	 * @param area
	 * @return
	 */
	public boolean inArea(int j, int i) {
		Point p = new Point();
		p.setLocation(j,i);
		
		return (leftEdge(p, area) && rightEdge(p, area) && topEdge(p, area) && bottomEdge(p, area));
		
		
}
/**
 * Este metodo retorna true si un punto que es pasado 
 * por parametro esta alineado verticalmente con algun
 * punto de la mascara que se encuentre por debajo del mismo.
 * De lo contrario retorna false.
 * @param p
 * @param area
 * @return
 */
private boolean bottomEdge(Point p, Hashtable<Integer,List<Integer>> area) {
	
	Integer x = new Integer((int) p.getX());
	if(area.containsKey(x))
	{
		for (int i = 0; i < area.get(x).size(); i++) {
			if (area.get(x).get(i).intValue() > p.getY())
				return true;
		}
	}
	return false;

	}
/**
 * Este metodo retorna true si un punto que es pasado 
 * por parametro esta alineado verticalmente con algun
 * punto de la mascara que se encuentre por encima del mismo.
 * De lo contrario retorna false.
 * @param p
 * @param area
 * @return
 */
private boolean topEdge(Point p, Hashtable<Integer,List<Integer>> area) {
	
	Integer x = new Integer((int) p.getX());
	if(area.containsKey(x))
	{
		for (int i = 0; i < area.get(x).size(); i++) {
			if (area.get(x).get(i).intValue() < p.getY())
				return true;
		}
	}
	return false;

	}
/**
 * Este metodo retorna true si un punto que es pasado 
 * por parametro esta alineado horizontalmente con algun
 * punto de la mascara que se encuentre mas a la izquierda del mismo.
 * De lo contrario retorna false.
 * @param p
 * @param area
 * @return
 */
private boolean leftEdge(Point p, Hashtable<Integer,List<Integer>> area) {
	
	Enumeration<Integer> e = area.keys();
	
	while (e.hasMoreElements())
	{
		Integer x = e.nextElement();
		if (area.get(x).contains((int)p.getY()))
		{
			if (x.intValue()<p.getX())
				return true;
		}
	}
	return false;
	}
/**
 * Este metodo retorna true si un punto que es pasado 
 * por parametro esta alineado horizontalmente con algun
 * punto de la mascara que se encuentre mas a la derecha del mismo.
 * De lo contrario retorna false.
 * @param p
 * @param area
 * @return
 */
private boolean rightEdge(Point p, Hashtable<Integer,List<Integer>> area) {
	
	Enumeration<Integer> e = area.keys();
	
	while (e.hasMoreElements())
	{
		Integer x = e.nextElement();
		if (area.get(x).contains((int)p.getY()))
		{
			if (x.intValue()>p.getX())
				return true;
		}
	}
	return false;
	}


/**
 * Este metodo retorna todos lo puntos que corresponden al o a las 
 * areas cerradas de la mascara.
 * @param mask
 * @param offset
 * @return
 */
	private Hashtable<Integer,List<Integer>> getMaskArea(BufferedImage mask) {
		Hashtable<Integer,List<Integer>> area = new Hashtable<Integer, List<Integer>>();

		for (int i = 0; i < mask.getHeight(); i++) {
			for (int j = 0; j < mask.getWidth(); j++) {
				if (mask.getRGB(j, i) != 0)
				{
					Integer x = new Integer(j);
					Integer y = new Integer(i);
					if (!area.containsKey(x))
					{
						ArrayList<Integer> yCoords = new ArrayList<Integer>();
						yCoords.add(y);
						area.put(x, yCoords);
					}
					else
					{	
						if (!area.get(x).contains(y))
						{
							area.get(x).add(y);
						}
					}
				}
			}
		}

		return area;
	}

public Point getOrigin() {
	return origin;
}

public void setOrigin(Point origin) {
	this.origin = origin;
}

public Point getOriginInf() {
	return originInf;
}

public void setOriginInf(Point originInf) {
	this.originInf = originInf;
}
	
	
}
